{
 "metadata": {
  "name": "Cython Magics",
  "signature": "sha256:c357b93e9480d6347c6677862bf43750745cef4b30129c5bc53cb879a19d4074"
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "heading",
     "level": 1,
     "metadata": {},
     "source": [
      "Cython Magic Functions"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Loading the extension"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Cython has an IPython extension that contains a number of magic functions for working with Cython code. This extension can be loaded using the `%load_ext` magic as follows:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%load_ext cython"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 1
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "The %cython_inline magic"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "The `%%cython_inline` magic uses `Cython.inline` to compile a Cython expression. This allows you to enter and run a function body with Cython code. Use a bare `return` statement to return values. "
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "a = 10\n",
      "b = 20"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 2
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%cython_inline\n",
      "return a+b"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 3,
       "text": [
        "30"
       ]
      }
     ],
     "prompt_number": 3
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "The %cython_pyximport magic"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "The `%%cython_pyximport` magic allows you to enter arbitrary Cython code into a cell. That Cython code is written as a `.pyx` file in the current working directory and then imported using `pyximport`.  You have the specify the name of the module that the Code will appear in. All symbols from the module are imported automatically by the magic function."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%cython_pyximport foo\n",
      "def f(x):\n",
      "    return 4.0*x"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 4
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "f(10)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 5,
       "text": [
        "40.0"
       ]
      }
     ],
     "prompt_number": 5
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "The %cython magic"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Probably the most important magic is the `%cython` magic.  This is similar to the `%%cython_pyximport` magic, but doesn't require you to specify a module name. Instead, the `%%cython` magic uses manages everything using temporary files in the `~/.cython/magic` directory.  All of the symbols in the Cython module are imported automatically by the magic.\n",
      "\n",
      "Here is a simple example of a Black-Scholes options pricing algorithm written in Cython. Please note that this example might not compile on non-POSIX systems (e.g., Windows) because of a missing `erf` symbol."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%cython\n",
      "cimport cython\n",
      "from libc.math cimport exp, sqrt, pow, log, erf\n",
      "\n",
      "@cython.cdivision(True)\n",
      "cdef double std_norm_cdf_cy(double x) nogil:\n",
      "    return 0.5*(1+erf(x/sqrt(2.0)))\n",
      "\n",
      "@cython.cdivision(True)\n",
      "def black_scholes_cy(double s, double k, double t, double v,\n",
      "                     double rf, double div, double cp):\n",
      "    \"\"\"Price an option using the Black-Scholes model.\n",
      "    \n",
      "    s : initial stock price\n",
      "    k : strike price\n",
      "    t : expiration time\n",
      "    v : volatility\n",
      "    rf : risk-free rate\n",
      "    div : dividend\n",
      "    cp : +1/-1 for call/put\n",
      "    \"\"\"\n",
      "    cdef double d1, d2, optprice\n",
      "    with nogil:\n",
      "        d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n",
      "        d2 = d1 - v*sqrt(t)\n",
      "        optprice = cp*s*exp(-div*t)*std_norm_cdf_cy(cp*d1) - \\\n",
      "            cp*k*exp(-rf*t)*std_norm_cdf_cy(cp*d2)\n",
      "    return optprice"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 6
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 7,
       "text": [
        "10.327861752731728"
       ]
      }
     ],
     "prompt_number": 7
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "For comparison, the same code is implemented here in pure python."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "from math import exp, sqrt, pow, log, erf\n",
      "\n",
      "def std_norm_cdf_py(x):\n",
      "    return 0.5*(1+erf(x/sqrt(2.0)))\n",
      "\n",
      "def black_scholes_py(s, k, t, v, rf, div, cp):\n",
      "    \"\"\"Price an option using the Black-Scholes model.\n",
      "    \n",
      "    s : initial stock price\n",
      "    k : strike price\n",
      "    t : expiration time\n",
      "    v : volatility\n",
      "    rf : risk-free rate\n",
      "    div : dividend\n",
      "    cp : +1/-1 for call/put\n",
      "    \"\"\"\n",
      "    d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n",
      "    d2 = d1 - v*sqrt(t)\n",
      "    optprice = cp*s*exp(-div*t)*std_norm_cdf_py(cp*d1) - \\\n",
      "        cp*k*exp(-rf*t)*std_norm_cdf_py(cp*d2)\n",
      "    return optprice"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 8
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 9,
       "text": [
        "10.327861752731728"
       ]
      }
     ],
     "prompt_number": 9
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Below we see the runtime of the two functions: the Cython version is nearly a factor of 10 faster."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%timeit black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "1000000 loops, best of 3: 319 ns per loop\n"
       ]
      }
     ],
     "prompt_number": 10
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%timeit black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "100000 loops, best of 3: 2.28 \u00b5s per loop\n"
       ]
      }
     ],
     "prompt_number": 11
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "External libraries"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Cython allows you to specify additional libraries to be linked with your extension, you can do so with the `-l` flag (also spelled `--lib`).  Note that this flag can be passed more than once to specify multiple libraries, such as `-lm -llib2 --lib lib3`.  Here's a simple example of how to access the system math library:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%cython -lm\n",
      "from libc.math cimport sin\n",
      "print 'sin(1)=', sin(1)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "sin(1)= 0.841470984808\n"
       ]
      }
     ],
     "prompt_number": 12
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "You can similarly use the `-I/--include` flag to add include directories to the search path, and `-c/--compile-args` to add extra flags that are passed to Cython via the `extra_compile_args` of the distutils `Extension` class.  Please see [the Cython docs on C library usage](https://docs.cython.org/src/tutorial/clibraries.html) for more details on the use of these flags."
     ]
    }
   ],
   "metadata": {}
  }
 ]
}
